package org.irri.smta.service.impl;

import java.util.Date;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.irri.smta.domain.SmtaReport;
import org.irri.smta.repository.ISmtaReportRepository;
import org.irri.smta.service.ISmtaReportService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

@Named("smtaReportService")
@Transactional(readOnly = true)
public class SmtaReportService implements ISmtaReportService<SmtaReport, Long> {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(SmtaReportService.class);

	@Inject
	@Named("smtaReportRepository")
	private ISmtaReportRepository smtaReportRepository;

	@Transactional(readOnly = true)
	@Override
	public Long getRowCount() {
		LOGGER.debug("Long getRowCount()");
		Long rowCount = smtaReportRepository.count();
		LOGGER.debug("rowCount = " + rowCount);
		return rowCount;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public SmtaReport create(SmtaReport smtaReport) {
		LOGGER.debug("SmtaReport create(SmtaReport smtaReport):" + smtaReport);
		Date now = new Date();
		smtaReport.setCreatedDate(now);
		smtaReport.setUpdatedDate(now);
		LOGGER.debug("Creating a new smtaReport with information: "
				+ smtaReport);
		return smtaReportRepository.save(smtaReport);
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public SmtaReport update(SmtaReport smtaReport) throws Exception {
		LOGGER.debug("SmtaReport update(SmtaReport smtaReport): " + smtaReport);
		smtaReport.setUpdatedDate(new Date());
		LOGGER.debug("Updating smtaReport with information: " + smtaReport);
		SmtaReport smtaReportDb = smtaReportRepository.findOne(smtaReport
				.getId());
		if (smtaReportDb == null) {
			LOGGER.debug("No smtaReport found with id: " + smtaReport.getId());
			throw new Exception();
		} else {
			delete(smtaReportDb.getId());
			smtaReportRepository.flush();
		}
		return smtaReportRepository.save(smtaReport);
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public SmtaReport delete(Long id) throws Exception {
		LOGGER.debug("SmtaReport delete(Long id): " + id);
		SmtaReport smtaReport = smtaReportRepository.findOne(id);
		if (smtaReport == null) {
			LOGGER.debug("No smtaReport found with id: " + id);
			throw new Exception();
		}
		smtaReportRepository.delete(smtaReport);
		return smtaReport;
	}

	@Transactional(readOnly = true)
	@Override
	public SmtaReport findById(Long id) {
		LOGGER.debug("findById(Long id): " + id);
		return smtaReportRepository.findOne(id);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAll() {
		LOGGER.debug("findAll()");
		List<SmtaReport> smtaReportList = smtaReportRepository.findAll();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAllOrdered() {
		LOGGER.debug("findAllOrdered()");
		List<SmtaReport> smtaReportList = smtaReportRepository.findAllOrdered();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAllUnreported() {
		LOGGER.debug("findAllUnreported()");
		List<SmtaReport> smtaReportList = smtaReportRepository
				.findAllUnreported();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAllReported() {
		LOGGER.debug("findAllReported()");
		List<SmtaReport> smtaReportList = smtaReportRepository
				.findAllReported();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findByPage(Integer pageIndex, Integer pageSize) {
		Page<SmtaReport> requestedPage = smtaReportRepository
				.findAll(constructPageSpecification(pageIndex, pageSize));
		List<SmtaReport> smtaReportList = requestedPage.getContent();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAll(Pageable pageable) {
		LOGGER.debug("List<SmtaReport> findAll(Pageable pageable)");
		Page<SmtaReport> page = smtaReportRepository.findAll(pageable);
		List<SmtaReport> smtaReportList = page.getContent();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findAll(Specification<SmtaReport> specification,
			Pageable pageable) {
		LOGGER.debug("List<SmtaReport> findAll(Specification<SmtaReport> specification, Pageable pageable)");
		Page<SmtaReport> page = smtaReportRepository.findAll(specification,
				pageable);
		List<SmtaReport> smtaReportList = page.getContent();
		return postProcess(smtaReportList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<SmtaReport> findBySymbol(String symbol) {
		List<SmtaReport> smtaReportList = smtaReportRepository
				.findBySymbol(symbol);
		return smtaReportList;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public SmtaReport updateReportedToTreaty(Long id, Boolean reportedToTreaty)
			throws Exception {
		SmtaReport smtaReport = smtaReportRepository.findOne(id);
		if (smtaReport != null) {
			smtaReport.setReportedToTreaty(reportedToTreaty);
			smtaReport.setReportedDate(new Date());
			smtaReport.setUpdatedDate(new Date());
			return smtaReportRepository.save(smtaReport);
		}
		return null;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public SmtaReport incrementReportAttempts(Long id) throws Exception {
		SmtaReport smtaReport = smtaReportRepository.findOne(id);
		if (smtaReport != null) {
			Long reportAttempts = smtaReport.getReportAttempts();
			if (reportAttempts != null) {
				reportAttempts++;
			}
			smtaReport.setReportAttempts(reportAttempts);
			smtaReport.setUpdatedDate(new Date());
			return smtaReportRepository.save(smtaReport);
		}
		return null;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public void setAsUreported(String symbol) throws Exception {
		List<SmtaReport> smtaReportList = smtaReportRepository
				.findBySymbol(symbol);
		if (smtaReportList.isEmpty() || smtaReportList.size() == 0) {
			return;
		}
		SmtaReport smtaReport = smtaReportList.get(0);
		if (smtaReport != null) {
			smtaReport.setReportedToTreaty(false);
			smtaReport.setUpdatedDate(new Date());
			smtaReportRepository.save(smtaReport);
		}
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public void setAsReported(String symbol) throws Exception {
		List<SmtaReport> smtaReportList = smtaReportRepository
				.findBySymbol(symbol);
		if (smtaReportList.isEmpty() || smtaReportList.size() == 0) {
			return;
		}
		SmtaReport smtaReport = smtaReportList.get(0);
		if (smtaReport != null) {
			smtaReport.setReportedToTreaty(true);
			smtaReport.setUpdatedDate(new Date());
			smtaReportRepository.save(smtaReport);
		}
	}

	/**
	 * Returns a new object which specifies the the wanted result page.
	 * 
	 * @param pageIndex
	 *            The index of the wanted result page
	 * @return
	 */
	private Pageable constructPageSpecification(Integer pageIndex,
			Integer pageSize) {
		Pageable pageSpecification = new PageRequest(pageIndex, pageSize,
				sortByIdAsc());
		return pageSpecification;
	}

	/**
	 * Returns a Sort object which sorts in ascending order
	 * 
	 * @return
	 */
	private Sort sortByIdAsc() {
		return new Sort(Sort.Direction.ASC, "id");
	}

	private List<SmtaReport> postProcess(List<SmtaReport> smtaReportList) {
		for (SmtaReport smtaReport : smtaReportList) {
			postProcess(smtaReport);
		}
		return smtaReportList;
	}

	private SmtaReport postProcess(SmtaReport smtaReport) {
		return smtaReport;
	}

}
